package Thread;

import java.util.Scanner;


//经典面试题--

//内存可见性引起的线程安全问题
//如果一个线程写，一个线程读，是否会引起线程安全问题呢？
//内存可见性高度依赖编译器的优化的具体实现，编译器啥时候触发优化是不确定的(尽量不要出现这种问题)
//意味着上述代码如果稍微改动可能结果截然不同，例如在循环里随便写一个休眠10ms，线程又能顺利结束了

public class TreadDemo19 {
    private volatile static int flag=0;
    //java中提供了volatile就可以使上诉优化被强制关闭，可以确保每次循环条件都会重新从内存中读取数据
    //就是强制读取内存，开销是大了，效率是低了，数据正确性/逻辑正确性也提高了

    public static void main(String[] args) {
        Thread t1=new Thread(()->{
            while(flag==0){

                /*try {
                    Thread.sleep(10);
                } catch (InterruptedException e) {
                    throw new RuntimeException(e);
                }*/
            }
            System.out.println("让t线程结束");
        });
        Thread t2=new Thread(()->{
            System.out.println("请输入flag的值:");
            Scanner scanner=new Scanner(System.in);
            flag=scanner.nextInt();
            //相当于t2修改了内存，但是t1没有看到这个内存的变化，就称为“内存可见性”问题
        });
        t1.start();
        t2.start();//t2要等待用户输入，无论t1先启动还是t2先启动，等待用户输入的过程中，t1必然都是已经循环很多次了
    }
}
//当我们输入一个非0的值的时候会发现t1并没有真的结束,当下这个情况，也是bug
//总的来说，一个线程循环，一个线程修改，在多线程的情况下，编译器对代码的优化做出了错误判断
//本来期待编译器把读内存的操作优化掉，变成读寄存器中缓存的值，这样的优化，有助于提高我们循环的执行效率
//并且编译器发现没有修改flag,编译器做了错误判断
//那么在这样的情况下，t2通过用户输入进行修改flag会导致并未在t1中生效，就出现了不能让t1顺利结束的bug


//volatile关有两个功能
//保证内存可见性，每次访问变量必须都要重新读取内存，而不会优化到寄存器/缓存器中
// 禁止指令重排序,针对被volatile修饰的变量的读写操作相关指令，是不能被重排序的